// ==================================================================================
// Shared Genomics Project MPI Codebase
// Version 1.0 30/04/2010
//
// (c) 2010 University of Manchester all rights reserved
//
// This file is distributed under the GNU General Public License, Version 2.  
// Please see the file COPYING.txt for more details
// ==================================================================================

/*! 
\file 
\brief Implementation of Individual-Major Allelic Bitstring array
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "BitString.h"
#include "copyfile.h"
#include "ipedfile.h"
#include "suffixes.h"

int ipedfile_create(struct selected_options *ops, struct pedfile *p) {
	FILE *out = NULL;
	BitString bs = NULL;
	int bsAllocatedStorageInBytes = 0, required_bits, nSNPs = 0, nSamples = 0, i, j;

	if (ops == NULL || p == NULL) return 0;
	else if (strlen(ops->szIpedFileName) == 0 || p->nSample == 0) return 0;
	else if (p->sample[0]->nOne == 0) return 0;

	required_bits = p->sample[0]->nOne * 2;

	// Allocate the bit string.
	bs = BitString_create(required_bits, &bsAllocatedStorageInBytes);
	if (bs == NULL) return 0;

	out = fopen(ops->szIpedFileName, "wb");
	if (out == NULL) {
		if (bs != NULL) free(bs);
		return 0;
	}

	nSamples = p->nSample;
	nSNPs = p->sample[0]->nOne;

	// Firstly write 3 'int' values to the file.
	// nSamples, nSNPs & bytes allocated to the bitstring.
	// Keeping the bitstring size incase meet any 64-bit weirdness down the line.
	if (fwrite(&nSamples, sizeof(int), 1, out) != 1) {
		if (out != NULL) fclose(out);
		if (bs != NULL) free(bs);
		return 0;
	}
	if (fwrite(&nSNPs, sizeof(int), 1, out) != 1) {
		if (out != NULL) fclose(out);
		if (bs != NULL) free(bs);
		return 0;
	}
	if (fwrite(&bsAllocatedStorageInBytes, sizeof(int), 1, out) != 1) {
		if (out != NULL) fclose(out);
		if (bs != NULL) free(bs);
		return 0;
	}

	// Write out the genomic data as a bitstring contains the diploid/haploid
	// boolean data of the study participants.
	for (i = 0; i < p->nSample; i++) {
		long bitpos = 0;

		BitStringset0(bs, 0, required_bits);
		for (j = 0; j < p->sample[i]->nOne && p->sample[i]->nOne == p->sample[i]->nTwo; j++) {
			if (p->sample[i]->one[j]) BitStringset1(bs, bitpos);
			bitpos++;
			if (p->sample[i]->two[j]) BitStringset1(bs, bitpos);
			bitpos++;
		}

		// bitstring to file.
		if (fwrite(bs, bsAllocatedStorageInBytes, 1, out) != 1) {
			if (out != NULL) fclose(out);
			if (bs != NULL) free(bs);
			return 0;
		} 
	}

	if (bs != NULL) free(bs);
	if (out != NULL) fclose(out);

	return 1;
}

char* ipedfile_load(struct selected_options *ops, int *nSamples, int *nSNPs, int *nRecordBytes) {
	char *bytes = NULL;
	FILE *in = NULL;
	int nRequiredBytes = 0;
	
	if (ops == NULL || nSamples == NULL || nSNPs == NULL || nRecordBytes == NULL) 
		return NULL;
	else if (strlen(ops->szIpedFileName) == 0) 
		return NULL;

	in = fopen(ops->szIpedFileName, "rb");
	if (in == NULL) return NULL;

	// Sample size
	if (fread(nSamples, sizeof(int), 1, in) != 1) {
		if (in != NULL) fclose(in);
		return NULL;
	}

	// No. SNPs
	if (fread(nSNPs, sizeof(int), 1, in) != 1) {
		if (in != NULL) fclose(in);
		return NULL;
	}

	// Record size in bytes
	if (fread(nRecordBytes, sizeof(int), 1, in) != 1) {
		if (in != NULL) fclose(in);
		return NULL;
	}

	// Load the remaining IPED file into memory.
	nRequiredBytes = (*nSamples) * (*nRecordBytes);
	bytes = (char*) malloc(nRequiredBytes);
	if (bytes != NULL) {
		// Failed to load the buffer into memory so leave deallocate
		// and quit.
		if (fread(bytes, nRequiredBytes, 1, in) != 1) {
			free(bytes);	
			return NULL;
		}
	}
	if (in != NULL) fclose(in);

	return bytes;
}

// Locally temporarily appending rank to the run ID to increase the uniqueness of the output file name.
int ipedfile_create_and_copy(struct selected_options *ops, struct pedfile *p, int rank) {
	BOOL jobDone = FALSE;
	char tmp_szRunId[TOKEN_LENGTH], remoteFilepath[FILE_PATH_LENGTH];

	if (p == NULL || ops == NULL) return 0;

	strcpy(tmp_szRunId, ops->szRunId);
	sprintf(ops->szRunId, "%s_%i", tmp_szRunId, rank);

	sprintf(ops->szIpedFileName, "%s%s%s_%i", ops->ldir, ops->szRunId, SUFFIX_IPED_FILE, rank);
	sprintf(remoteFilepath, "%s%s%s", ops->rdir, tmp_szRunId, SUFFIX_IPED_FILE);
	
	if (ipedfile_create(ops, p))  if (copyAndDeleteFile(ops->szIpedFileName, remoteFilepath)) jobDone = TRUE;
	
	strcpy(ops->szRunId, tmp_szRunId); // Restore the original Run ID

	if (jobDone) return 1;
	else return 0;
}